package com.smedi.ismedi.inmail.core.application.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.Common;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiAttendanceListRequest;
import com.dingtalk.api.request.OapiUserListidRequest;
import com.dingtalk.api.request.OapiV2DepartmentListsubRequest;
import com.dingtalk.api.request.OapiV2UserGetRequest;
import com.dingtalk.api.response.OapiAttendanceListResponse;
import com.dingtalk.api.response.OapiUserListidResponse;
import com.dingtalk.api.response.OapiV2DepartmentListsubResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.smedi.ismedi.constructionproject.api.dto.request.ConstructionProjectUpdateRequest;
import com.smedi.ismedi.constructionproject.api.dto.response.resp.ProjectWithDingResp;
import com.smedi.ismedi.constructionproject.api.feign.RemoteConstructionProjectFeignApi;
import com.smedi.ismedi.inmail.core.application.command.DingAttendanceResultCommand;
import com.smedi.ismedi.inmail.core.application.command.DingDeptUserSaveCommand;
import com.smedi.ismedi.inmail.core.application.command.DingDingDept;
import com.smedi.ismedi.inmail.core.application.convert.AppConverter;
import com.smedi.ismedi.inmail.core.application.query.vo.*;
import com.smedi.ismedi.inmail.core.application.service.OverviewApplicationService;
import com.smedi.ismedi.inmail.core.domain.dto.PersonQuery;
import com.smedi.ismedi.inmail.core.domain.repository.DingAttendanceResultRepository;
import com.smedi.ismedi.inmail.core.domain.repository.DingDeptUserRepository;
import com.smedi.ismedi.inmail.core.infrastructure.constant.ConstantField;
import com.smedi.ismedi.inmail.core.infrastructure.constant.DingDing;
import com.smedi.ismedi.inmail.core.infrastructure.util.JsonUtil;
import com.smedi.ismedi.inmail.core.port.adapter.persistence.model.DingAttendanceResultEntity;
import com.smedi.ismedi.inmail.core.port.adapter.service.FileRemoteApi;
import com.smedi.ismedi.inmail.core.port.adapter.service.PersonRemoteApi;
import com.smedi.ismedi.organization.api.dto.request.HrOrgUpdateRequest;
import com.smedi.ismedi.organization.api.feign.RemoteHrOrganizationFeignApi;
import com.smedi.ismedi.personnel.api.dto.response.resp.PersonResp;
import com.smedi.ismedi.web.starter.common.ResponseData;
import com.taobao.api.ApiException;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 通知公告应用服务实现类
 *
 * @author jiangqinxuan
 * @date 2021/07/26
 */
@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class OverviewApplicationServiceImpl implements OverviewApplicationService {
    private final FileRemoteApi fileRemoteApi;

    private final RemoteHrOrganizationFeignApi remoteHrOrganizationFeignApi;

    private final RemoteConstructionProjectFeignApi remoteConstructionProjectFeignApi;

    private final AppConverter appConverter;

    private final DingDeptUserRepository dingDeptUserRepository;

    private final DingAttendanceResultRepository dingAttendanceResultRepository;

    private final PersonRemoteApi personRemoteApi;


    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    @SneakyThrows
    public String exportProjectOverview(List<ProjectOverviewVo> projectOverviews) {
        String fileKey = "";
        ClassPathResource classPathResource = new ClassPathResource("fileTemplate/projectOverview.xlsx");
        XSSFWorkbook workBook;
        try (InputStream inputStream = classPathResource.getInputStream()) {
            workBook = new XSSFWorkbook(inputStream);
        }
        XSSFSheet buildingSheet = workBook.cloneSheet(0, "项目统计");
        XSSFCellStyle cellStyle = workBook.createCellStyle();
        //启动单元格内换行
        cellStyle.setWrapText(true);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        int i = 0;
        for (ProjectOverviewVo projectOverviewVo : projectOverviews) {
            i++;
            XSSFRow rowAdd = buildingSheet.createRow(i);
            rowAdd.setHeight((short) 600);
            for (int j = 0; j < 11; j++) {
                rowAdd.createCell(j);
                rowAdd.getCell(j).setCellStyle(cellStyle);
            }
            rowAdd.getCell(0).setCellValue(i);
            rowAdd.getCell(1).setCellValue(projectOverviewVo.getName());
            rowAdd.getCell(2).setCellValue(String.valueOf(projectOverviewVo.getTotalNum()));
            rowAdd.getCell(3).setCellValue(String.valueOf(projectOverviewVo.getNotStartedNum()));
            rowAdd.getCell(4).setCellValue(String.valueOf(projectOverviewVo.getConstructingNum()));
            rowAdd.getCell(5).setCellValue(String.valueOf(projectOverviewVo.getStoppedNum()));
            rowAdd.getCell(6).setCellValue(String.valueOf(projectOverviewVo.getFinishedNum()));
            rowAdd.getCell(7).setCellValue(String.valueOf(projectOverviewVo.getCompletedNum()));
            rowAdd.getCell(8).setCellValue(String.valueOf(projectOverviewVo.getSelfSupportNum()));
            rowAdd.getCell(9).setCellValue(String.valueOf(projectOverviewVo.getConsortiumNum()));
            rowAdd.getCell(10).setCellValue(String.valueOf(projectOverviewVo.getSelfSupportConsortiumNum()));
        }

        workBook.removeSheetAt(0);

        final File outputFile = File.createTempFile(IdUtil.simpleUUID(), ".xlsx");
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            workBook.write(outputStream);
            fileKey = fileRemoteApi.uploadFile(outputFile);
        } catch (Exception e) {
            log.info(e.getMessage());
        } finally {
            Files.deleteIfExists(Paths.get(outputFile.getAbsolutePath()));
        }

        return fileKey;
    }

    @Override
    @SneakyThrows
    public String exportSafetyCheckOrganizationOverview(List<SafetyCheckOrganizationOverviewVo> safetyCheckOrganizationOverviewVos) {
        String fileKey = "";
        ClassPathResource classPathResource = new ClassPathResource("fileTemplate/safetyCheckOrganizationOverview.xlsx");
        XSSFWorkbook workBook;
        try (InputStream inputStream = classPathResource.getInputStream()) {
            workBook = new XSSFWorkbook(inputStream);
        }
        XSSFSheet buildingSheet = workBook.cloneSheet(0, "项目统计");
        XSSFCellStyle cellStyle = workBook.createCellStyle();
        //启动单元格内换行
        cellStyle.setWrapText(true);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        int i = 0;
        for (SafetyCheckOrganizationOverviewVo safetyCheckOrganizationOverviewVo : safetyCheckOrganizationOverviewVos) {
            i++;
            XSSFRow rowAdd = buildingSheet.createRow(i);
            rowAdd.setHeight((short) 600);
            for (int j = 0; j < 8; j++) {
                rowAdd.createCell(j);
                rowAdd.getCell(j).setCellStyle(cellStyle);
            }
            rowAdd.getCell(0).setCellValue(i);
            rowAdd.getCell(1).setCellValue(safetyCheckOrganizationOverviewVo.getName());
            rowAdd.getCell(2).setCellValue(String.valueOf(safetyCheckOrganizationOverviewVo.getTotalNum()));
            rowAdd.getCell(3).setCellValue(String.valueOf(safetyCheckOrganizationOverviewVo.getCheckProjectNum()));
            rowAdd.getCell(4).setCellValue(String.valueOf(safetyCheckOrganizationOverviewVo.getCheckMajorNum()));
            rowAdd.getCell(5).setCellValue(String.valueOf(safetyCheckOrganizationOverviewVo.getCheckLeaderNum()));
            rowAdd.getCell(6).setCellValue(String.valueOf(safetyCheckOrganizationOverviewVo.getHiddenDangerNum()));
            rowAdd.getCell(7).setCellValue(String.valueOf(safetyCheckOrganizationOverviewVo.getRectificationRate()));
        }

        workBook.removeSheetAt(0);

        final File outputFile = File.createTempFile(IdUtil.simpleUUID(), ".xlsx");
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            workBook.write(outputStream);
            fileKey = fileRemoteApi.uploadFile(outputFile);
        } catch (Exception e) {
            log.info(e.getMessage());
        } finally {
            Files.deleteIfExists(Paths.get(outputFile.getAbsolutePath()));
        }

        return fileKey;
    }

    @Override
    @SneakyThrows
    public String exportSafetyCheckProjectOverview(List<SafetyCheckProjectOverviewVo> safetyCheckProjectOverviewVos) {
        String fileKey = "";
        ClassPathResource classPathResource = new ClassPathResource("fileTemplate/safetyCheckProjectOverview.xlsx");
        XSSFWorkbook workBook;
        try (InputStream inputStream = classPathResource.getInputStream()) {
            workBook = new XSSFWorkbook(inputStream);
        }
        XSSFSheet buildingSheet = workBook.cloneSheet(0, "项目统计");
        XSSFCellStyle cellStyle = workBook.createCellStyle();
        //启动单元格内换行
        cellStyle.setWrapText(true);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        int i = 0;
        for (SafetyCheckProjectOverviewVo safetyCheckProjectOverviewVo : safetyCheckProjectOverviewVos) {
            i++;
            XSSFRow rowAdd = buildingSheet.createRow(i);
            rowAdd.setHeight((short) 600);
            for (int j = 0; j < 8; j++) {
                rowAdd.createCell(j);
                rowAdd.getCell(j).setCellStyle(cellStyle);
            }
            rowAdd.getCell(0).setCellValue(i);
            rowAdd.getCell(1).setCellValue(safetyCheckProjectOverviewVo.getName());
            rowAdd.getCell(2).setCellValue(String.valueOf(safetyCheckProjectOverviewVo.getTotalNum()));
            rowAdd.getCell(3).setCellValue(String.valueOf(safetyCheckProjectOverviewVo.getCheckProjectNum()));
            rowAdd.getCell(4).setCellValue(String.valueOf(safetyCheckProjectOverviewVo.getCheckMajorNum()));
            rowAdd.getCell(5).setCellValue(String.valueOf(safetyCheckProjectOverviewVo.getCheckLeaderNum()));
            rowAdd.getCell(6).setCellValue(String.valueOf(safetyCheckProjectOverviewVo.getHiddenDangerNum()));
            rowAdd.getCell(7).setCellValue(String.valueOf(safetyCheckProjectOverviewVo.getRectificationRate()));
        }

        workBook.removeSheetAt(0);

        final File outputFile = File.createTempFile(IdUtil.simpleUUID(), ".xlsx");
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            workBook.write(outputStream);
            fileKey = fileRemoteApi.uploadFile(outputFile);
        } catch (Exception e) {
            log.info(e.getMessage());
        } finally {
            Files.deleteIfExists(Paths.get(outputFile.getAbsolutePath()));
        }

        return fileKey;
    }

    @Override
    @SneakyThrows
    public String exportVideoMonitorOrganizationOverview(List<VideoMonitorOrganizationOverviewVo> videoMonitorOrganizationOverviewVos) {
        String fileKey = "";
        ClassPathResource classPathResource = new ClassPathResource("fileTemplate/videoMonitorOrganizationOverview.xlsx");
        XSSFWorkbook workBook;
        try (InputStream inputStream = classPathResource.getInputStream()) {
            workBook = new XSSFWorkbook(inputStream);
        }
        XSSFSheet buildingSheet = workBook.cloneSheet(0, "项目统计");
        XSSFCellStyle cellStyle = workBook.createCellStyle();
        //启动单元格内换行
        cellStyle.setWrapText(true);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        int i = 0;
        for (VideoMonitorOrganizationOverviewVo videoMonitorOrganizationOverviewVo : videoMonitorOrganizationOverviewVos) {
            i++;
            XSSFRow rowAdd = buildingSheet.createRow(i);
            rowAdd.setHeight((short) 600);
            for (int j = 0; j < 11; j++) {
                rowAdd.createCell(j);
                rowAdd.getCell(j).setCellStyle(cellStyle);
            }
            rowAdd.getCell(0).setCellValue(i);
            rowAdd.getCell(1).setCellValue(videoMonitorOrganizationOverviewVo.getName());
            rowAdd.getCell(2).setCellValue(String.valueOf(videoMonitorOrganizationOverviewVo.getProjectNum()));
            rowAdd.getCell(3).setCellValue(String.valueOf(videoMonitorOrganizationOverviewVo.getNotInstallNum()));
            rowAdd.getCell(4).setCellValue(String.valueOf(videoMonitorOrganizationOverviewVo.getNeedNum()));
            rowAdd.getCell(5).setCellValue(String.valueOf(videoMonitorOrganizationOverviewVo.getExistNum()));
            rowAdd.getCell(6).setCellValue(String.valueOf(videoMonitorOrganizationOverviewVo.getStoppedNum()));
            rowAdd.getCell(7).setCellValue(String.valueOf(videoMonitorOrganizationOverviewVo.getNeedButNoExistNum()));
            rowAdd.getCell(8).setCellValue(String.valueOf(videoMonitorOrganizationOverviewVo.getVideoNum()));
            rowAdd.getCell(9).setCellValue(String.valueOf(videoMonitorOrganizationOverviewVo.getInlineNum()));
            rowAdd.getCell(10).setCellValue(String.valueOf(videoMonitorOrganizationOverviewVo.getOutlineNum()));
        }

        workBook.removeSheetAt(0);

        final File outputFile = File.createTempFile(IdUtil.simpleUUID(), ".xlsx");
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            workBook.write(outputStream);
            fileKey = fileRemoteApi.uploadFile(outputFile);
        } catch (Exception e) {
            log.info(e.getMessage());
        } finally {
            Files.deleteIfExists(Paths.get(outputFile.getAbsolutePath()));
        }

        return fileKey;
    }

    @Override
    @SneakyThrows
    public String exportVideoMonitorProjectOverview(List<VideoMonitorProjectOverviewVo> videoMonitorProjectOverviewVos) {
        String fileKey = "";
        ClassPathResource classPathResource = new ClassPathResource("fileTemplate/videoMonitorProjectOverview.xlsx");
        XSSFWorkbook workBook;
        try (InputStream inputStream = classPathResource.getInputStream()) {
            workBook = new XSSFWorkbook(inputStream);
        }
        XSSFSheet buildingSheet = workBook.cloneSheet(0, "项目统计");
        XSSFCellStyle cellStyle = workBook.createCellStyle();
        //启动单元格内换行
        cellStyle.setWrapText(true);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        int i = 0;
        for (VideoMonitorProjectOverviewVo videoMonitorProjectOverviewVo : videoMonitorProjectOverviewVos) {
            i++;
            XSSFRow rowAdd = buildingSheet.createRow(i);
            rowAdd.setHeight((short) 600);
            for (int j = 0; j < 8; j++) {
                rowAdd.createCell(j);
                rowAdd.getCell(j).setCellStyle(cellStyle);
            }
            rowAdd.getCell(0).setCellValue(i);
            rowAdd.getCell(1).setCellValue(videoMonitorProjectOverviewVo.getOrganizationName());
            rowAdd.getCell(2).setCellValue(String.valueOf(videoMonitorProjectOverviewVo.getName()));
            rowAdd.getCell(3).setCellValue(String.valueOf(ConstantField.ProjectStatusEnum.getValue(videoMonitorProjectOverviewVo.getProjectStatus())));
            rowAdd.getCell(4).setCellValue(String.valueOf(ConstantField.MonitorOnlineStatusEnum.getValue(videoMonitorProjectOverviewVo.getMonitorOnlineStatus())));
            rowAdd.getCell(5).setCellValue(null != videoMonitorProjectOverviewVo.getVideoNum() ? String.valueOf(videoMonitorProjectOverviewVo.getVideoNum()) : "-");
            rowAdd.getCell(6).setCellValue(null != videoMonitorProjectOverviewVo.getInlineNum() ? String.valueOf(videoMonitorProjectOverviewVo.getInlineNum()) : "-");
            rowAdd.getCell(7).setCellValue(null != videoMonitorProjectOverviewVo.getOutlineNum() ? String.valueOf(videoMonitorProjectOverviewVo.getOutlineNum()) : "-");
        }

        workBook.removeSheetAt(0);

        final File outputFile = File.createTempFile(IdUtil.simpleUUID(), ".xlsx");
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            workBook.write(outputStream);
            fileKey = fileRemoteApi.uploadFile(outputFile);
        } catch (Exception e) {
            log.info(e.getMessage());
        } finally {
            Files.deleteIfExists(Paths.get(outputFile.getAbsolutePath()));
        }

        return fileKey;
    }

    @Override
    public String exportAttendanceOverview(List<AttendanceOverviewVo> attendanceOverviews) {
        return null;
    }

    @Override
    @SneakyThrows
    public String exportDingAttendanceList(List<DingAttendanceOverViewVo> dingAttendanceOverViewVos) {
        String fileKey = "";
        ClassPathResource classPathResource = new ClassPathResource("fileTemplate/orgDingAttendanceOverView.xlsx");
        XSSFWorkbook workBook;
        try (InputStream inputStream = classPathResource.getInputStream()) {
            workBook = new XSSFWorkbook(inputStream);
        }
        XSSFSheet buildingSheet = workBook.cloneSheet(0, "主体院统计");
        XSSFCellStyle cellStyle = workBook.createCellStyle();
        //启动单元格内换行
        cellStyle.setWrapText(true);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        int i = 0;
        for (DingAttendanceOverViewVo dingAttendanceOverViewVo : dingAttendanceOverViewVos) {
            i++;
            XSSFRow rowAdd = buildingSheet.createRow(i);
            rowAdd.setHeight((short) 600);
            for (int j = 0; j < 10; j++) {
                rowAdd.createCell(j);
                rowAdd.getCell(j).setCellStyle(cellStyle);
            }
            rowAdd.getCell(0).setCellValue(i);
            rowAdd.getCell(1).setCellValue(dingAttendanceOverViewVo.getOrgName());
            rowAdd.getCell(2).setCellValue(dingAttendanceOverViewVo.getEvaluate());
            rowAdd.getCell(3).setCellValue(String.valueOf(dingAttendanceOverViewVo.getUnPassProjectNumber()));
            rowAdd.getCell(4).setCellValue(String.valueOf(dingAttendanceOverViewVo.getProjectNumber()));
            rowAdd.getCell(5).setCellValue(String.valueOf(dingAttendanceOverViewVo.getReportPersonNumber()));
            rowAdd.getCell(6).setCellValue(String.valueOf(dingAttendanceOverViewVo.getPassReportPersonNumber()));
            rowAdd.getCell(7).setCellValue(String.valueOf(dingAttendanceOverViewVo.getUnPassReportPersonNumber()));
            rowAdd.getCell(8).setCellValue(String.valueOf(dingAttendanceOverViewVo.getAttendanceNumber()));
            rowAdd.getCell(9).setCellValue(String.valueOf(dingAttendanceOverViewVo.getAvgAttendanceFloat()));
        }
        workBook.removeSheetAt(0);
        final File outputFile = File.createTempFile(IdUtil.simpleUUID(), ".xlsx");
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            workBook.write(outputStream);
            fileKey = fileRemoteApi.uploadFile(outputFile);
        } catch (Exception e) {
            log.info(e.getMessage());
        } finally {
            Files.deleteIfExists(Paths.get(outputFile.getAbsolutePath()));
        }

        return fileKey;
    }

    @Override
    @SneakyThrows
    public String exportDingAttendanceProjectList(List<DingAttendanceProjectOverViewVo> voList) {
        String fileKey = "";
        ClassPathResource classPathResource = new ClassPathResource("fileTemplate/projectDingAttendanceOverView.xlsx");
        XSSFWorkbook workBook;
        try (InputStream inputStream = classPathResource.getInputStream()) {
            workBook = new XSSFWorkbook(inputStream);
        }
        XSSFSheet buildingSheet = workBook.cloneSheet(0, "项目统计");
        XSSFCellStyle cellStyle = workBook.createCellStyle();
        //启动单元格内换行
        cellStyle.setWrapText(true);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        int i = 0;
        for (DingAttendanceProjectOverViewVo dingAttendanceOverViewVo : voList) {
            i++;
            XSSFRow rowAdd = buildingSheet.createRow(i);
            rowAdd.setHeight((short) 600);
            for (int j = 0; j < 9; j++) {
                rowAdd.createCell(j);
                rowAdd.getCell(j).setCellStyle(cellStyle);
            }
            rowAdd.getCell(0).setCellValue(i);
            rowAdd.getCell(1).setCellValue(dingAttendanceOverViewVo.getOrgName());
            rowAdd.getCell(2).setCellValue(dingAttendanceOverViewVo.getProjectName());
            rowAdd.getCell(3).setCellValue(dingAttendanceOverViewVo.getEvaluate());
            rowAdd.getCell(4).setCellValue(String.valueOf(dingAttendanceOverViewVo.getReportPersonNumber()));
            rowAdd.getCell(5).setCellValue(String.valueOf(dingAttendanceOverViewVo.getPassReportPersonNumber()));
            rowAdd.getCell(6).setCellValue(String.valueOf(dingAttendanceOverViewVo.getUnPassReportPersonNumber()));
            rowAdd.getCell(7).setCellValue(String.valueOf(dingAttendanceOverViewVo.getAttendanceNumber()));
            rowAdd.getCell(8).setCellValue(String.valueOf(dingAttendanceOverViewVo.getAvgAttendanceFloat()));
        }
        workBook.removeSheetAt(0);
        final File outputFile = File.createTempFile(IdUtil.simpleUUID(), ".xlsx");
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            workBook.write(outputStream);
            fileKey = fileRemoteApi.uploadFile(outputFile);
        } catch (Exception e) {
            log.info(e.getMessage());
        } finally {
            Files.deleteIfExists(Paths.get(outputFile.getAbsolutePath()));
        }

        return fileKey;
    }

    @Override
    @SneakyThrows
    public void updateDingTalkData() {
        Client client = createClient();
        List<String> userIds = dingDeptUserRepository.getUserIds();
        if(CollectionUtils.isEmpty(userIds)){
            return;
        }
        String token = getAccessToken(client);
        int size = userIds.size()/5;
        for(int i=1;i<=size;i++){
            runAsync(token, userIds.subList((i-1)*5,i*5));
            Thread.sleep(1000);
        }
    }

    @Override
    @SneakyThrows
    public void updateDingTalkPerson() {
        Client client = createClient();
        String accessToken = getAccessToken(client);
        if (accessToken != null) {
            //获取院级数据
            List<DingDingDept> dingDingDepts = getDingDingList(accessToken, null);
            if (!CollectionUtils.isEmpty(dingDingDepts)) {
                Map<String, Long> map = getAllUserDetails();
                for (DingDingDept dingDingDept : dingDingDepts) {
                    HrOrgUpdateRequest hrOrgUpdateRequest = appConverter.dingDingDeptToHrOrgUpdateRequest(dingDingDept);
                    //更新院级数据，更新成功后继续更新其下属项目数据
                    ResponseData<Long> responseData = remoteHrOrganizationFeignApi.updateByName(hrOrgUpdateRequest);
                    if (responseData != null && responseData.getData() != null && responseData.getData() > 0) {
                        //获取院级用户ID列表
                        List<String> userIds = getUserIdsByDeptId(hrOrgUpdateRequest.getDingDeptKey(), accessToken);
                        //更新到数据库
                        updateUserIdsByDingDeptKey(hrOrgUpdateRequest.getDingDeptKey(), userIds);
                        //将钉钉用户ID与本地库用户ID关联
                        saveImlUserId(hrOrgUpdateRequest.getDingDeptKey(), accessToken, map);
                        //更新下属项目级数据
                        Thread.sleep(1000);
                        updateProjectDingDingData(accessToken, hrOrgUpdateRequest, map);

                    } else {
                        log.error("钉钉组织数据定时更新，组织：{}未找到", hrOrgUpdateRequest.getShortName());
                    }
                    Thread.sleep(1000);
                }
            }
        } else {
            log.error("定时更新组织项目失败，原因：accessToken获取失败");
        }
    }

    private void runAsync(String accessToken, List<String> userId) {
        getPersonalAttendanceResult(userId, accessToken, 0L, 50L, LocalDate.now().minusDays(1) + " 00:00:00", LocalDate.now() + " 00:00:00");
        //getPersonalAttendanceResult(userId, accessToken, 0L, 50L, LocalDate.now().minusDays(13) + " 00:00:00", LocalDate.now().minusDays(7) + " 23:59:59");
        //getPersonalAttendanceResult(userId, accessToken, 0L, 50L, LocalDate.now().minusDays(20) + " 00:00:00", LocalDate.now().minusDays(14) + " 23:59:59");
        //getPersonalAttendanceResult(userId, accessToken, 0L, 50L, LocalDate.now().minusDays(27) + " 00:00:00", LocalDate.now().minusDays(21) + " 23:59:59");
        //getPersonalAttendanceResult(userId, accessToken, 0L, 50L, LocalDate.now().minusDays(34) + " 00:00:00", LocalDate.now().minusDays(28) + " 23:59:59");
        //getPersonalAttendanceResult(userId, accessToken, 0L, 50L, LocalDate.now().minusDays(41) + " 00:00:00", LocalDate.now().minusDays(35) + " 23:59:59");
    }

    /**
     * 将从钉钉获取的项目级deptId和用户id更新到数据库
     */
    private void updateProjectDingDingData(String accessToken, HrOrgUpdateRequest hrOrgUpdateRequest, Map<String, Long> map) throws ApiException {
        //获取项目级数据
        List<DingDingDept> projects = getDingDingList(accessToken, hrOrgUpdateRequest.getDingDeptKey());
        if (!CollectionUtils.isEmpty(projects)) {
            for (DingDingDept dingDept : projects) {
                ConstructionProjectUpdateRequest constructionProjectUpdateRequest = appConverter.dingDingDeptToConstructionProjectUpdateRequest(dingDept);
                ResponseData<Long> projectUpdate = remoteConstructionProjectFeignApi.updateByName(constructionProjectUpdateRequest);
                if (projectUpdate == null || projectUpdate.getData() == null || projectUpdate.getData() == 0) {
                    log.error("钉钉组织数据定时更新，项目：{}未找到", constructionProjectUpdateRequest.getProjectName());
                    continue;
                }
                //获取项目级用户ID列表
                List<String> userList = getUserIdsByDeptId(constructionProjectUpdateRequest.getDingDeptKey(), accessToken);
                //更新到数据库
                updateUserIdsByDingDeptKey(constructionProjectUpdateRequest.getDingDeptKey(), userList);
                //将钉钉用户ID与本地库用户ID关联
                saveImlUserId(constructionProjectUpdateRequest.getDingDeptKey(), accessToken, map);
            }
        }
    }

    /**
     * 获取钉钉链接Client
     */
    public static Client createClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new Client(config);
    }

    /**
     * 获取Token
     */
    public static String getAccessToken(Client client) {
        GetAccessTokenRequest getAccessTokenRequest = new GetAccessTokenRequest()
                .setAppKey(DingDing.APP_KEY)
                .setAppSecret(DingDing.APP_SECRET);
        try {
            return client.getAccessToken(getAccessTokenRequest).getBody().accessToken;
        } catch (TeaException err) {
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("钉钉错误码：{}，钉钉错误信息：{}", err.code, err.message);
            }

        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("钉钉错误码：{}，钉钉错误信息：{}", err.code, err.message);
            }
        }
        return null;
    }

    /**
     * 根据Token获取钉钉部门列表
     *
     * @param deptId
     * @param accessToken
     * @return 部门列表
     */
    private List<DingDingDept> getDingDingList(String accessToken, String deptId) throws ApiException {
        DingTalkClient client = new DefaultDingTalkClient(DingDing.DEPT_LIST_URL);
        OapiV2DepartmentListsubRequest request = new OapiV2DepartmentListsubRequest();
        if (deptId != null) {
            request.setDeptId(Long.valueOf(deptId));
        }
        OapiV2DepartmentListsubResponse response = client.execute(request, accessToken);
        JSONObject object = JSONObject.parseObject(response.getBody());
        if (object.getInteger("errcode") == 0) {
            return JsonUtil.jsonToList(object.getString("result"), DingDingDept.class);
        }
        log.error("钉钉错误码：{}，错误信息：{}", object.getInteger("errcode"), object.getString("errmsg"));
        return null;
    }


    /**
     * 根据部门ID获取钉钉上用户ID列表
     *
     * @param deptId
     * @param accessToken
     * @return ID列表
     */
    private List<String> getUserIdsByDeptId(String deptId, String accessToken) {
        try {
            DingTalkClient client = new DefaultDingTalkClient(DingDing.USER_ID_LIST_URL);
            OapiUserListidRequest req = new OapiUserListidRequest();
            req.setDeptId(Long.valueOf(deptId));
            OapiUserListidResponse rsp = client.execute(req, accessToken);
            JSONObject object = JSONObject.parseObject(rsp.getBody());
            if (object.getInteger("errcode") == 0) {
                return JsonUtil.jsonToList(object.getJSONObject("result").getString("userid_list"), String.class);
            }
            log.error("钉钉错误码：{}，错误信息：{}", object.getInteger("errcode"), object.getString("errmsg"));
        } catch (Exception e) {
            log.error("获取钉钉用户ID列表失败，失败原因：{}", e.getMessage());
        }
        return null;
    }

    /**
     * 根据部门ID更新与部门有关联的用户ID
     */
    private void updateUserIdsByDingDeptKey(String deptId, List<String> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            log.info("钉钉部门或组织{}下用户Id为空", deptId);
            return;
        }
        //获取数据库中该组织下用户id
        List<String> userIdsInDb = dingDeptUserRepository.getUserIdsByDeptKey(deptId);
        //如果没有直接插入
        if (CollectionUtils.isEmpty(userIdsInDb)) {
            dingDeptUserRepository.batchSave(userIds, deptId);
            log.info("本地数据库部门或组织{}下用户为空，直接插入", deptId);
        } else {
            //比较两个list,钉钉拉取的数据中如果没有，那么数据库中的应该删除
            List<String> deleteIds = userIdsInDb.stream().filter(s -> !userIds.contains(s)).collect(Collectors.toList());//要删除的id
            if (!CollectionUtils.isEmpty(deleteIds)) {
                dingDeptUserRepository.batchDelete(deleteIds, deptId);
                //去除数据库中不变的
                userIdsInDb.removeAll(deleteIds);
                log.info("本地数据库部门或组织{}下同步删除钉钉中已被删除用户", deptId);
            }
            //需要新增的
            userIds.removeAll(userIdsInDb);
            if (!CollectionUtils.isEmpty(userIds)) {
                dingDeptUserRepository.batchSave(userIds, deptId);
                log.info("本地数据库部门或组织{}下同步新增钉钉中增加的用户", deptId);
            }


        }
    }

    @Override
    public Boolean personExist(Long personId) {
        return dingDeptUserRepository.personExist(personId);
    }

    @Override
    public List<String> peopleByProjectId(Long projectId) {
        ResponseData<ProjectWithDingResp> projectWithDingResp = remoteConstructionProjectFeignApi.getProjectWithDing(projectId);
        if(projectWithDingResp != null && projectWithDingResp.getData() != null && !StringUtils.isEmpty(projectWithDingResp.getData().getDingDeptKey())){
            return dingDeptUserRepository.peopleByProjectId(projectWithDingResp.getData().getDingDeptKey());
        }
        return new ArrayList<>();
    }

    /**
     * 将钉钉用户ID，部门与本地用户ID相关联
     */
    private void saveImlUserId(String deptId, String accessToken, Map<String, Long> map) {
        //获取数据库中数据
        List<String> userIdsInDb = dingDeptUserRepository.getUserIdsByDeptKey(deptId);
        if (CollectionUtils.isEmpty(userIdsInDb)) {
            log.info("未查询到钉钉部门ID为：{}相关联的本地库数据", deptId);
            return;
        }
        //获取ismedi本地所有用户
        if (map == null) {
            log.info("远程获取库中用户数据列表失败，暂时无法将本地库中用户ID与钉钉ID关联");
            return;
        }
        //获取钉钉用户手机号
        List<DingDeptUserSaveCommand> commandList = new ArrayList<>();
        for (String id : userIdsInDb) {
            DingDeptUserSaveCommand command = new DingDeptUserSaveCommand();
            command.setDingDeptKey(deptId);
            String mobile = getDingDingUserMobile(id, accessToken);
            command.setUserId(id);
            Long ismediUserId = map.get(mobile);
            if (ismediUserId != null) {
                command.setIsmediUserId(ismediUserId);
            }
            setCommandNameAndJob(command, id, accessToken);
            if (command.getName() == null && command.getIsmediUserId() == null) {
                continue;
            }
            commandList.add(command);
        }
        if (CollectionUtils.isEmpty(commandList)) {
            log.info("暂时没有需要关联的用户ID");
            return;
        }
        dingDeptUserRepository.batchSaveImlUserId(commandList);
    }

    /**
     * 根据用户名获取手机号
     */
    private void setCommandNameAndJob(DingDeptUserSaveCommand command, String userId, String accessToken) {
        try {
            DingTalkClient defaultDingTalkClient = new DefaultDingTalkClient(DingDing.USER_DETAIL_URL);
            OapiV2UserGetRequest req = new OapiV2UserGetRequest();
            req.setUserid(userId);
            OapiV2UserGetResponse rsp = defaultDingTalkClient.execute(req, accessToken);
            JSONObject object = JSONObject.parseObject(rsp.getBody());
            if (object.getInteger("errcode") == 0) {
                JSONObject userDetail = object.getJSONObject("result");
                command.setName(userDetail.getString("name"));
                command.setProjectJob(userDetail.getString("title"));
            } else {
                log.error("钉钉错误码：{}，错误信息：{}", object.getInteger("errcode"), object.getString("errmsg"));
            }
        } catch (Exception e) {
            log.error("获取钉钉用户手机号失败，失败原因：{}", e.getMessage());
        }
    }

    /**
     * 根据钉钉用户ID获取钉钉用户手机号
     */
    private String getDingDingUserMobile(String userId, String accessToken) {
        try {
            DingTalkClient client = new DefaultDingTalkClient(DingDing.USER_DETAIL_URL);
            OapiV2UserGetRequest req = new OapiV2UserGetRequest();
            req.setUserid(userId);
            OapiV2UserGetResponse rsp = client.execute(req, accessToken);
            JSONObject object = JSONObject.parseObject(rsp.getBody());
            if (object.getInteger("errcode") == 0) {
                JSONObject userDetail = object.getJSONObject("result");
                return userDetail.getString("mobile");
            }
            log.error("钉钉错误码：{}，错误信息：{}", object.getInteger("errcode"), object.getString("errmsg"));
        } catch (Exception e) {
            log.error("获取钉钉用户手机号失败，失败原因：{}", e.getMessage());
        }
        return null;
    }

    /**
     * 获取本系统所有用户
     */
    private Map<String, Long> getAllUserDetails() {
        //获取本系统所有用户并转为手机号：用户Id的map
        List<PersonResp> personResps = personRemoteApi.getPersonList(PersonQuery.builder().isDelete(false).build());
        if (!CollectionUtils.isEmpty(personResps)) {
            return personResps.stream().filter(personResp -> !ObjectUtils.isEmpty(personResp.getMobile()))
                    .filter(personResp -> !ObjectUtils.isEmpty(personResp.getId()))
                    .collect(Collectors.toMap(PersonResp::getMobile, PersonResp::getId, (key1, key2) -> key2));
        }
        return null;
    }

    /**
     * 获取钉钉个人考勤数据
     */
    private void getPersonalAttendanceResult(List<String> userId, String accessToken, Long offset, Long limit, String startTime, String endTime) {
        try {

            DingTalkClient client = new DefaultDingTalkClient(DingDing.USER_ATTENDANCE_URL);
            OapiAttendanceListRequest req = new OapiAttendanceListRequest();
            req.setUserIdList(userId);
            req.setOffset(offset);
            req.setLimit(limit);
            req.setWorkDateTo(endTime);
            req.setWorkDateFrom(startTime);
            OapiAttendanceListResponse rsp = client.execute(req, accessToken);
            JSONObject object = JSONObject.parseObject(rsp.getBody());
            if (object.getInteger("errcode") == 0) {
                String attendanceResult = object.getString("recordresult");
                if (attendanceResult.isEmpty()) {
                    log.error("用户{}并未查询到{}到{}之间的考勤记录", userId, startTime, endTime);
                    return;
                }
                List<DingAttendanceResultCommand> dingAttendanceResultCommands = mapper.readValue(attendanceResult, new TypeReference<List<DingAttendanceResultCommand>>() {
                });
                if (CollectionUtils.isEmpty(dingAttendanceResultCommands)) {
                    log.error("用户{}并未查询到{}到{}之间的考勤记录", userId, startTime, endTime);
                    return;
                }
                List<DingAttendanceResultEntity> dingAttendanceResultEntities = dingAttendanceResultCommands.stream().map(appConverter::dingAttendanceResultCommandToDingAttendanceResultEntity).collect(Collectors.toList());
                dingAttendanceResultRepository.batchSave(dingAttendanceResultEntities);
                if (object.getBoolean("hasMore")) {
                    getPersonalAttendanceResult(userId, accessToken, offset + limit, limit, startTime, endTime);
                }
            } else {
                log.error("钉钉错误码：{}，错误信息：{}", object.getInteger("errcode"), object.getString("errmsg"));
            }


        } catch (Exception e) {
            log.error("获取钉钉用户考勤数据失败，失败原因：{}", e.getMessage());
        }
    }

}
